package com.xyzwps.server.paimon.ui

import com.xyzwps.server.paimon.error.AppException
import com.xyzwps.server.paimon.error.Errors
import jakarta.servlet.http.HttpServletRequest
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.context.MessageSource
import org.springframework.context.i18n.LocaleContextHolder
import org.springframework.http.HttpStatus
import org.springframework.http.ProblemDetail
import org.springframework.validation.FieldError
import org.springframework.web.bind.MethodArgumentNotValidException
import org.springframework.web.bind.ServletRequestBindingException
import org.springframework.web.bind.annotation.ControllerAdvice
import org.springframework.web.bind.annotation.ExceptionHandler
import java.net.URI

@ControllerAdvice
class MvcExceptionHandlers(private val messageSource: MessageSource) {

    companion object {
        private val log: Logger = LoggerFactory.getLogger(MvcExceptionHandlers::class.java)
    }

    @ExceptionHandler(AppException::class)
    fun appExceptionHandler(e: AppException): ProblemDetail {
        val locale = LocaleContextHolder.getLocale()
        val message = messageSource.getMessage("err.${e.code}", null, locale)
        val p = ProblemDetail.forStatus(e.status)
        p.type = URI.create("/errors/${e.code}")
        p.detail = message
        p.title = e.status.name
        return p
    }

    @ExceptionHandler(Exception::class)
    fun fallbackExceptionHandler(request: HttpServletRequest, ex: Throwable): ProblemDetail {
        return when (ex) {
            is MethodArgumentNotValidException -> handleException(ex)
            is ServletRequestBindingException -> handleException(ex)
            else -> fallback(ex)
        }
    }

    private fun handleException(e: ServletRequestBindingException): ProblemDetail {
        return fallback(e)
    }

    private fun handleException(e: MethodArgumentNotValidException): ProblemDetail {
        val locale = LocaleContextHolder.getLocale()
        val problem = e.body
        val br = e.bindingResult
        val error = br.allErrors.first()
        val parameterName = if (error is FieldError) error.field else error.objectName
        val message = messageSource.getMessage("Parameter", null, locale) +
                " $parameterName ${error.defaultMessage}"
        problem.detail = message
        return problem
    }


    private fun fallback(ex: Throwable): ProblemDetail {
        log.error("Unhandled exception", ex)
        val problem = ProblemDetail.forStatus(HttpStatus.INTERNAL_SERVER_ERROR)
        problem.title = ex.javaClass.canonicalName
        problem.detail = ex.message
        return problem
    }

}
